-
Notifications
You must be signed in to change notification settings - Fork 134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat!: Allow validators outside the active set to validate on consumer chains #1878
feat!: Allow validators outside the active set to validate on consumer chains #1878
Conversation
This reverts commit 2e5a91a.
store := ctx.KVStore(k.storeKey) | ||
bz, err := validator.Marshal() | ||
if err != nil { | ||
panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err)) |
Check warning
Code scanning / CodeQL
Panic in BeginBock or EndBlock consensus methods Warning
iterator.Value() | ||
var validator types.ConsumerValidator | ||
if err := validator.Unmarshal(iterator.Value()); err != nil { | ||
panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)) |
Check warning
Code scanning / CodeQL
Panic in BeginBock or EndBlock consensus methods Warning
@@ -498,7 +504,7 @@ func New( | |||
// NOTE: Any module instantiated in the module manager that is later modified | |||
// must be passed by reference here. | |||
app.MM = module.NewManager( | |||
genutil.NewAppModule( | |||
wrapped_genutil.NewAppModule( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Genutil and staking need to be wrapped, because they send validator updates to CometBFT and we need to filter those.
WalkthroughWalkthroughThe changes introduced in this update modify how validators interact with the consensus and validation process in the Interchain Security Module. Validators not participating in the consensus on the provider chain can now validate consumer chains. This involves new functionalities to manage validator sets, import statements, module initialization, and adjustments in tests to accommodate the new features. Changes
Sequence Diagram(s)sequenceDiagram
participant Provider
participant Consumer
participant Validator
Note over Provider,Validator: Initial Setup
Provider->>Validator: Determine Active Set
Provider->Consumer: Send Validator Set (including inactive validators)
Consumer->>Validator: Include Inactive Validators
Note over Provider,Consumer: Ongoing Validation
Consumer->>Provider: Sync status with inactive validators
Note over Validator: Validation on Consumer Chain
Validator->>Consumer: Validate blocks (inactive on Provider)
Consumer->>Provider: Report validation status (periodically)
Assessment against linked issues
The above changes and additions comprehensively address the objectives outlined in the linked issues, ensuring validators not in the provider's consensus can still validate consumer chains, alongside various tests and parameter adjustments to support this functionality. Recent review detailsConfiguration used: .coderabbit.yml Files selected for processing (2)
Additional context usedPath-based instructions (2)
GitHub Check: CodeQL
Additional comments not posted (2)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @p-offtermatt. The general logic LGTM. See my comment below.
Things missing:
- migration (e.g., the
LastProviderConsensusValSet
needs to be initialized) - docs on integration (i.e., how to wire this in app.go, especially given the wrapped modules)
- the provider module needs to implement the staking keeper interface, similarly to the consumer module (some of the modules that use the staking keeper need to use the provider keeper instead, e.g., mint, gov, distribution, IBC, ... )
- finalized ADR
wrapped_genutil "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_genutil" | ||
wrapped_staking "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_staking" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to find better naming for these two modules.
@@ -217,17 +219,18 @@ func (k Keeper) SendVSCPacketsToChain(ctx sdk.Context, chainID, channelID string | |||
func (k Keeper) QueueVSCPackets(ctx sdk.Context) { | |||
valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID | |||
|
|||
// get the bonded validators from the staking module |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This docstring is still relevant. Explain better why bondedValidators
is needed and why consensusValidators
is needed (i.e., for TopN chains).
totalPower := k.stakingKeeper.GetLastTotalPower(ctx) | ||
|
||
// Get total total power of the last consensus validator set on the provider | ||
totalPower := k.GetLastTotalProviderConsensusPower(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this changes the behavior. stakingKeeper.GetLastTotalPower
gets the total power from the store, while GetLastTotalProviderConsensusPower
iterates over 180 keys and recomputes the power every time.
|
||
// InitGenesis delegates the InitGenesis call to the underlying x/genutil module, | ||
// however, it returns no validator updates as validator updates will be provided by the provider module. | ||
func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed? We don't have a wrapped genutil in the democracy module on the consumer. How is this different?
} | ||
} | ||
|
||
k.SetLastProviderConsensusValSet(ctx, reducedValSet) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also be exported in ExportGenesis
} | ||
for _, val := range bondedValidators[:maxValidators] { | ||
// create the validator from the staking validator | ||
consAddr, err := val.GetConsAddr() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could potentially reuse CreateConsumerValidator
with an empty chainID
.
"error", err) | ||
continue | ||
} | ||
nextValidator := types.ConsumerValidator{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General comment but maybe we should not be calling those **Consumer**
validators anymore because they're also provider validators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, agreed. Didn't think of a good, unambigious way for this yet
@@ -262,11 +262,14 @@ func (k Keeper) MakeConsumerGenesis( | |||
// get the bonded validators from the staking module | |||
bondedValidators := k.stakingKeeper.GetLastValidators(ctx) | |||
|
|||
// get the consensus validators (to compute the minimal power in the top N) | |||
consensusValidators := k.GetLastProviderConsensusValSet(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need both consensus and bonded validators?
|
||
reducedValSet := make([]types.ConsumerValidator, len(valSet)) | ||
for i, val := range valSet { | ||
consAddr, err := val.GetConsAddr() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See previous comment on potentially using CreateConsumerValidator
with empty chainID
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 25
Outside diff range and nitpick comments (11)
x/ccv/provider/module_test.go (1)
Line range hint
12-12
: Please reorder the imports according to the Golang style guide.- "github.com/golang/mock/gomock" + "github.com/cosmos/ibc-go/v7/modules/core/24-host" + "github.com/golang/mock/gomock"Tools
golangci-lint
18-18: File is not
gci
-ed with --skip-generated -s standard -s default -s blank -s dot -s prefix(cosmossdk.io) -s prefix(github.com/cosmos/cosmos-sdk) -s prefix(github.com/cometbft/cometbft) -s prefix(github.com/cosmos/interchain-security) --custom-order (gci)x/ccv/provider/module.go (1)
Line range hint
30-30
: TheAppModule
struct needs to implement theOnAcknowledgementPacket
method to satisfy theIBCModule
interface.+ func (am AppModule) OnAcknowledgementPacket(ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) (*sdk.Result, error) { + // Implementation goes here + return nil, nil + }x/ccv/provider/keeper/partial_set_security.go (1)
Line range hint
92-103
: Refactor to use more efficient sorting and power summing methods.The current method for computing the minimum power to opt in is inefficient as it sorts the entire list of validator powers and then computes a running total. Consider using a min-heap for a more efficient approach.
import "container/heap" // Use a min-heap to keep track of the smallest powers, which allows us to quickly access the top N powers. type IntHeap []int64 func (h IntHeap) Len() int { return len(h) } func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *IntHeap) Push(x interface{}) { *h = append(*h, x.(int64)) } func (h *IntHeap) Pop() interface{} { old := *h n := len(old) x := old[n-1] *h = old[0 : n-1] return x } // In your function: powersHeap := &IntHeap{} heap.Init(powersHeap) for _, val := range bondedValidators { heap.Push(powersHeap, val.Power) if powersHeap.Len() > int(topN) { heap.Pop(powersHeap) } } minPowerToOptIn := (*powersHeap)[0] // The smallest power in the top N return minPowerToOptIn, nilx/ccv/provider/keeper/validator_set_update_test.go (2)
Line range hint
233-233
: Consider making "chainID" a constant to avoid repetition.- chainID := "chainID" + const chainID = "chainID"
Line range hint
321-332
: Refactor repeated validator creation code into a shared helper function.- valA := testkeeper.CreateStakingValidator(ctx, mocks, 1, 1) - valB := testkeeper.CreateStakingValidator(ctx, mocks, 2, 2) + valA, valB := createTestValidators(ctx, mocks, []int{1, 2}, []int{1, 2}) // Helper function func createTestValidators(ctx sdk.Context, mocks MockedKeepers, indices []int, powers []int64) ([]stakingtypes.Validator, []crypto.PublicKey) { var validators []stakingtypes.Validator var publicKeys []crypto.PublicKey for i, index := range indices { validator, publicKey := testkeeper.CreateStakingValidator(ctx, mocks, index, powers[i]) validators = append(validators, validator) publicKeys = append(publicKeys, publicKey) } return validators, publicKeys }Also applies to: 363-375, 410-410, 429-429, 444-444
x/ccv/provider/keeper/proposal.go (4)
Line range hint
281-281
: Undefined functionDiffValidators
detected.Please ensure that the function
DiffValidators
is defined or imported correctly in this file. If it's defined in another package, you might need to import it.
Line range hint
30-30
: Ensure theKeeper
type is defined or imported correctly.It seems there's a recurring issue with the
Keeper
type being undefined. Please check the import statements and ensure that theKeeper
type is correctly defined and accessible in this context.
Line range hint
53-53
: Ensure theKeeper
type is defined or imported correctly.This issue with the
Keeper
type being undefined appears multiple times. It's crucial to resolve this to prevent runtime errors.
Line range hint
129-129
: Ensure theKeeper
type is defined or imported correctly.The recurring issue with the
Keeper
type being undefined needs thorough investigation. Please check all relevant import statements and definitions.x/ccv/provider/keeper/proposal_test.go (1)
Line range hint
1128-1128
: The variablefound
is reassigned but never used afterwards, which could be an oversight or unnecessary code.- found = providerKeeper.PendingConsumerRemovalPropExists( - ctx, invalidProp.ChainId, invalidProp.StopTime) - require.False(t, found) + _ = providerKeeper.PendingConsumerRemovalPropExists( + ctx, invalidProp.ChainId, invalidProp.StopTime)Tools
golangci-lint
10-10: File is not
gci
-ed with --skip-generated -s standard -s default -s blank -s dot -s prefix(cosmossdk.io) -s prefix(github.com/cosmos/cosmos-sdk) -s prefix(github.com/cometbft/cometbft) -s prefix(github.com/cosmos/interchain-security) --custom-order (gci)tests/e2e/config.go (1)
90-90
: Add documentation forInactiveValsCfg
.It would be beneficial to add a comment explaining the purpose and usage of the
InactiveValsCfg
configuration type, especially since it relates to the new feature of allowing inactive validators.
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(valD, true).AnyTimes() | ||
|
||
mocks.MockStakingKeeper.EXPECT().GetLastValidators(ctx).Return([]stakingtypes.Validator{valA, valB, valC, valD}).AnyTimes() | ||
stakingValA := createStakingValidator(ctx, mocks, 1, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function createStakingValidator
is called multiple times but is not defined in this file or imported. This will cause a compilation error.
To resolve this, ensure that createStakingValidator
is either defined in this file or properly imported from another package. If it's intended to be a helper function, you might need to define it like this:
func createStakingValidator(ctx sdk.Context, mocks testkeeper.MockedKeepers, index int, power int64) stakingtypes.Validator {
// Implementation details here
}
Also applies to: 125-125, 131-131, 137-137, 161-161, 219-219, 222-222, 225-225, 228-228, 309-309, 312-312, 315-315, 318-318, 321-321, 381-381, 391-391
Tools
golangci-lint
119-119: undefined: createStakingValidator (typecheck)
GitHub Check: tests
[failure] 119-119:
undefined: createStakingValidator
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types" | ||
providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove duplicate import of "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
.
- "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
- providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
+ providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types"
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types" | |
providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" | |
providertypes "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" |
Tools
golangci-lint
25-25: ST1019: package "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" is being imported more than once (stylecheck)
26-26: ST1019(related information): other import of "github.com/cosmos/interchain-security/v4/x/ccv/provider/types" (stylecheck)
"sort" | ||
"testing" | ||
"time" | ||
|
||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please organize imports according to the Golang style guide using gci
.
- stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+ stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
Committable suggestion was skipped due to low confidence.
Tools
golangci-lint
10-10: File is not
gci
-ed with --skip-generated -s standard -s default -s blank -s dot -s prefix(cosmossdk.io) -s prefix(github.com/cosmos/cosmos-sdk) -s prefix(github.com/cometbft/cometbft) -s prefix(github.com/cosmos/interchain-security) --custom-order (gci)
x/ccv/provider/keeper/keeper.go
Outdated
validators := k.GetLastProviderConsensusValSet(ctx) | ||
|
||
// if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch | ||
bondedValidators := k.stakingKeeper.GetLastValidators(ctx) | ||
if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { | ||
// in a Top-N chain, we automatically opt in all validators that belong to the top N | ||
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN) | ||
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, validators, topN) | ||
if err == nil { | ||
k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) | ||
k.OptInTopNValidators(ctx, chainID, validators, minPower) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider renaming validators
to providerValidators
for clarity.
- validators := k.GetLastProviderConsensusValSet(ctx)
+ providerValidators := k.GetLastProviderConsensusValSet(ctx)
This change would make the variable name more descriptive and align with the suggestion from a previous review comment.
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
validators := k.GetLastProviderConsensusValSet(ctx) | |
// if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch | |
bondedValidators := k.stakingKeeper.GetLastValidators(ctx) | |
if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { | |
// in a Top-N chain, we automatically opt in all validators that belong to the top N | |
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, bondedValidators, topN) | |
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, validators, topN) | |
if err == nil { | |
k.OptInTopNValidators(ctx, chainID, bondedValidators, minPower) | |
k.OptInTopNValidators(ctx, chainID, validators, minPower) | |
validators := k.GetLastProviderConsensusValSet(ctx) | |
// if the validator was not part of the last epoch, check if the validator is going to be part of te next epoch | |
if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 { | |
// in a Top-N chain, we automatically opt in all validators that belong to the top N | |
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, validators, topN) | |
if err == nil { | |
k.OptInTopNValidators(ctx, chainID, validators, minPower) |
wrapped_genutil "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_genutil" | ||
wrapped_staking "github.com/cosmos/interchain-security/v4/x/ccv/wrapped_staking" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider renaming wrapped_genutil
and wrapped_staking
for clarity.
The current names wrapped_genutil
and wrapped_staking
are not very descriptive. Consider using names that reflect their functionality or the reason why they are wrapped, which could improve code readability and maintainability.
store := ctx.KVStore(k.storeKey) | ||
bz, err := validator.Marshal() | ||
if err != nil { | ||
panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace panics with proper error handling in the setValidator
and getValSet
methods to prevent potential chain halts if errors occur. This will enhance the robustness and fault tolerance of the consensus-related methods.
- panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err))
+ return fmt.Errorf("failed to marshal ConsumerValidator: %w", err)
- panic(fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err))
+ return fmt.Errorf("failed to unmarshal ConsumerValidator: %w", err)
Also applies to: 89-89
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
panic(fmt.Errorf("failed to marshal ConsumerValidator: %w", err)) | |
return fmt.Errorf("failed to marshal ConsumerValidator: %w", err) |
Tools
GitHub Check: CodeQL
[warning] 26-26: Panic in BeginBock or EndBlock consensus methods
Possible panics in BeginBock- or EndBlock-related consensus methods could cause a chain halt
// get the staking validator set | ||
valSet := k.stakingKeeper.GetLastValidators(ctx) | ||
|
||
// restrict the set to the first MaxProviderConsensusValidators | ||
maxVals := k.GetParams(ctx).MaxProviderConsensusValidators | ||
if int64(len(valSet)) > maxVals { | ||
valSet = valSet[:maxVals] | ||
} | ||
|
||
reducedValSet := make([]types.ConsumerValidator, len(valSet)) | ||
for i, val := range valSet { | ||
consAddr, err := val.GetConsAddr() | ||
if err != nil { | ||
k.Logger(ctx).Error("could not create consumer validator", | ||
"validator", val.GetOperator().String(), | ||
"error", err) | ||
continue | ||
} | ||
pubKey, err := val.TmConsPublicKey() | ||
if err != nil { | ||
k.Logger(ctx).Error("could not create consumer validator", | ||
"validator", val.GetOperator().String(), | ||
"error", err) | ||
continue | ||
} | ||
reducedValSet[i] = types.ConsumerValidator{ | ||
ProviderConsAddr: consAddr, | ||
Power: k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()), | ||
ConsumerPublicKey: &pubKey, | ||
} | ||
} | ||
|
||
k.SetLastProviderConsensusValSet(ctx, reducedValSet) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic to restrict the validator set to the first MaxProviderConsensusValidators
is implemented correctly. However, consider adding more detailed logging or error handling around the slicing operation to ensure that any issues with the validator set size are clearly reported and can be debugged easily.
if int64(len(valSet)) > maxVals {
+ k.Logger(ctx).Info("Trimming validator set size", "original_size", len(valSet), "max_size", maxVals)
valSet = valSet[:maxVals]
}
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
// get the staking validator set | |
valSet := k.stakingKeeper.GetLastValidators(ctx) | |
// restrict the set to the first MaxProviderConsensusValidators | |
maxVals := k.GetParams(ctx).MaxProviderConsensusValidators | |
if int64(len(valSet)) > maxVals { | |
valSet = valSet[:maxVals] | |
} | |
reducedValSet := make([]types.ConsumerValidator, len(valSet)) | |
for i, val := range valSet { | |
consAddr, err := val.GetConsAddr() | |
if err != nil { | |
k.Logger(ctx).Error("could not create consumer validator", | |
"validator", val.GetOperator().String(), | |
"error", err) | |
continue | |
} | |
pubKey, err := val.TmConsPublicKey() | |
if err != nil { | |
k.Logger(ctx).Error("could not create consumer validator", | |
"validator", val.GetOperator().String(), | |
"error", err) | |
continue | |
} | |
reducedValSet[i] = types.ConsumerValidator{ | |
ProviderConsAddr: consAddr, | |
Power: k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()), | |
ConsumerPublicKey: &pubKey, | |
} | |
} | |
k.SetLastProviderConsensusValSet(ctx, reducedValSet) | |
// get the staking validator set | |
valSet := k.stakingKeeper.GetLastValidators(ctx) | |
// restrict the set to the first MaxProviderConsensusValidators | |
maxVals := k.GetParams(ctx).MaxProviderConsensusValidators | |
if int64(len(valSet)) > maxVals { | |
k.Logger(ctx).Info("Trimming validator set size", "original_size", len(valSet), "max_size", maxVals) | |
valSet = valSet[:maxVals] | |
} | |
reducedValSet := make([]types.ConsumerValidator, len(valSet)) | |
for i, val := range valSet { | |
consAddr, err := val.GetConsAddr() | |
if err != nil { | |
k.Logger(ctx).Error("could not create consumer validator", | |
"validator", val.GetOperator().String(), | |
"error", err) | |
continue | |
} | |
pubKey, err := val.TmConsPublicKey() | |
if err != nil { | |
k.Logger(ctx).Error("could not create consumer validator", | |
"validator", val.GetOperator().String(), | |
"error", err) | |
continue | |
} | |
reducedValSet[i] = types.ConsumerValidator{ | |
ProviderConsAddr: consAddr, | |
Power: k.stakingKeeper.GetLastValidatorPower(ctx, val.GetOperator()), | |
ConsumerPublicKey: &pubKey, | |
} | |
} | |
k.SetLastProviderConsensusValSet(ctx, reducedValSet) |
@@ -685,7 +685,7 @@ func TestValidateGenesisState(t *testing.T) { | |||
nil, | |||
types.NewParams(ibctmtypes.NewClientState("", ibctmtypes.DefaultTrustLevel, 0, 0, | |||
time.Second*40, clienttypes.Height{}, commitmenttypes.GetSDKSpecs(), []string{"ibc", "upgradedIBCState"}), | |||
types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600), | |||
types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600, 180), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check the validity of the consumer registration fee parameters.
- sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}
+ sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}
- sdk.Coin{Denom: "stake", Amount: sdk.NewInt(-1000000)}
+ sdk.Coin{Denom: "stake", Amount: sdk.NewInt(1000000)}
Ensure that the denomination and amount are correctly set for the consumer registration fee.
Also applies to: 709-709
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "st", Amount: sdk.NewInt(10000000)}, 600, 180), | |
types.DefaultTrustingPeriodFraction, time.Hour, time.Hour, 30*time.Minute, time.Hour, "0.1", sdk.Coin{Denom: "stake", Amount: sdk.NewInt(10000000)}, 600, 180), |
abci "github.com/cometbft/cometbft/abci/types" | ||
"github.com/cosmos/interchain-security/v4/x/ccv/provider/types" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure the Keeper
type is correctly defined and accessible in this file.
It appears that the Keeper
type might not be defined or imported correctly, as indicated by the static analysis tool. Please verify the definition and import statements.
@@ -217,17 +219,18 @@ | |||
func (k Keeper) QueueVSCPackets(ctx sdk.Context) { | |||
valUpdateID := k.GetValidatorSetUpdateId(ctx) // current valset update ID | |||
|
|||
// get the bonded validators from the staking module | |||
// get the last validator set sent to consensus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review the usage of undefined functions and ensure they are correctly defined or imported.
The functions DiffValidators
and other operations related to validator set management are referenced but not defined in the visible scope. Ensure these functions are properly defined or imported to avoid runtime errors.
Also applies to: 239-239, 303-303
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Outside diff range and nitpick comments (2)
x/ccv/provider/keeper/partial_set_security_test.go (2)
119-142
: Check for potential redundant mock expectationsThe
GetValidatorByConsAddr
method ofMockStakingKeeper
is expected to be called any number of times with specific addresses. This setup could potentially be simplified or explained better to ensure that it's clear why it's needed for each validator and that it does not mask other potential issues in the test logic.
Line range hint
381-391
: Optimization suggestion for list manipulationsWhen manipulating lists such as allowlists and denylists, consider checking if operations can be optimized or if there are redundant operations that could be streamlined. This can improve the performance and readability of the test code.
@@ -1298,13 +1298,14 @@ func (k Keeper) HasToValidate( | |||
return true, nil | |||
} | |||
|
|||
providerValidators := k.GetLastProviderConsensusValSet(ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential data race and inefficiency in validator handling.
The function HasToValidate
retrieves the validator set multiple times which can lead to inefficiencies and potential data races in a concurrent environment. Consider retrieving the validator set once at the beginning of the function and reusing it throughout.
+ providerValidators := k.GetLastProviderConsensusValSet(ctx)
if topN, found := k.GetTopN(ctx, chainID); found && topN > 0 {
minPower, err := k.ComputeMinPowerToOptIn(ctx, chainID, providerValidators, topN)
if err == nil {
k.OptInTopNValidators(ctx, chainID, providerValidators, minPower)
}
}
Also applies to: 1306-1306
@@ -257,7 +257,7 @@ | |||
|
|||
var minPowerInTopN int64 | |||
if found && topN > 0 { | |||
res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.stakingKeeper.GetLastValidators(ctx), topN) | |||
res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.GetLastProviderConsensusValSet(ctx), topN) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optimize error handling in logging.
Currently, the error message in the logging statement does not include the error object, which could provide valuable debugging information. Consider adding the error to the log output to improve error visibility and diagnostics.
- k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err)
+ k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err.Error())
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.GetLastProviderConsensusValSet(ctx), topN) | |
res, err := k.ComputeMinPowerToOptIn(ctx, chainID, k.GetLastProviderConsensusValSet(ctx), topN) | |
if err != nil { | |
k.Logger(ctx).Error("failed to compute min power to opt in for chain", "chain", chainID, "error", err.Error()) | |
return nil, err | |
} |
stakingValA := testkeeper.CreateStakingValidator(ctx, mocks, 1, 1) | ||
valA, err := providerKeeper.CreateConsumerValidator(ctx, "chainID", stakingValA) // 10% of the total voting power (can opt out) | ||
require.NoError(t, err) | ||
valAConsAddr := valA.ProviderConsAddr | ||
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valAConsAddr).Return(stakingValA, true).AnyTimes() | ||
|
||
stakingValB := testkeeper.CreateStakingValidator(ctx, mocks, 2, 2) | ||
valB, err := providerKeeper.CreateConsumerValidator(ctx, "chainID", stakingValB) // 20% of the total voting power (can opt out) | ||
require.NoError(t, err) | ||
valBConsAddr := valB.ProviderConsAddr | ||
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valBConsAddr).Return(stakingValB, true).AnyTimes() | ||
|
||
stakingValC := testkeeper.CreateStakingValidator(ctx, mocks, 3, 3) | ||
valC, err := providerKeeper.CreateConsumerValidator(ctx, "chainID", stakingValC) // 30% of the total voting power (cannot opt out) | ||
require.NoError(t, err) | ||
valCConsAddr := valC.ProviderConsAddr | ||
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valCConsAddr).Return(stakingValC, true).AnyTimes() | ||
|
||
stakingValD := testkeeper.CreateStakingValidator(ctx, mocks, 4, 4) | ||
valD, err := providerKeeper.CreateConsumerValidator(ctx, "chainID", stakingValD) // 40% of the total voting power (cannot opt out) | ||
require.NoError(t, err) | ||
valDConsAddr := valD.ProviderConsAddr | ||
mocks.MockStakingKeeper.EXPECT().GetValidatorByConsAddr(ctx, valDConsAddr).Return(stakingValD, true).AnyTimes() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider defining the CreateStakingValidator
function or ensuring its import
The function CreateStakingValidator
is used multiple times in this test file but is not defined or imported in the visible scope. This could potentially lead to a compilation error unless it is defined elsewhere that's not shown here. Ensure that this function is either defined within this test file or properly imported from the testkeeper
package.
|
||
return []abci.ValidatorUpdate{} | ||
// EndBlock logic needed for the Validator Set Update sub-protocol | ||
return am.keeper.EndBlockVSU(ctx) |
Check warning
Code scanning / CodeQL
Panic in BeginBock or EndBlock consensus methods Warning
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
path flow from Begin/EndBlock to a panic call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Outside diff range and nitpick comments (1)
x/ccv/provider/keeper/relay.go (1)
Line range hint
149-171
: RefactorEndBlockVSU
for clarity and error handling.The
EndBlockVSU
method is complex and handles multiple responsibilities. Consider refactoring it to improve readability and maintainability. Additionally, ensure that all operations have appropriate error handling to prevent potential issues during the block end logic.func (k Keeper) EndBlockVSU(ctx sdk.Context) []abci.ValidatorUpdate { + var err error providerUpdates := k.ProviderValidatorUpdates(ctx) if providerUpdates == nil { + return nil } k.completeMaturedUnbondingOps(ctx) if ctx.BlockHeight()%k.GetBlocksPerEpoch(ctx) == 0 { k.QueueVSCPackets(ctx) - k.SendVSCPackets(ctx) + if err = k.SendVSCPackets(ctx); err != nil { + k.Logger(ctx).Error("Failed to send VSC packets", "error", err) + } } return providerUpdates }
am.keeper.InitGenesis(ctx, &genesisState) | ||
|
||
return []abci.ValidatorUpdate{} | ||
return am.keeper.InitGenesis(ctx, &genesisState) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure proper error handling in InitGenesis
.
Currently, the InitGenesis
method does not handle any errors that might occur during the initialization process. It's important to ensure that any potential issues are properly addressed to prevent runtime errors.
- return am.keeper.InitGenesis(ctx, &genesisState)
+ updates, err := am.keeper.InitGenesis(ctx, &genesisState)
+ if err != nil {
+ return nil, err
+ }
+ return updates, nil
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
return am.keeper.InitGenesis(ctx, &genesisState) | |
return am.keeper.InitGenesis(ctx, &genesisState) | |
updates, err := am.keeper.InitGenesis(ctx, &genesisState) | |
if err != nil { | |
return nil, err | |
} | |
return updates, nil |
Closing because this has been rebased in PRs targeting the v5 release with Cosmos SDK v50 |
Description
Closes: #1913
This PR allows validators that are not participating in consensus on the provider chain to validate on consumer chains.
The extremely high level overview can be found here (I recommend reading this to get the high-level idea):
https://informalsystems.notion.site/Non-active-validators-on-consumer-chains-d4b700e19a7d447c8d9a2e69cca3e8d0?pvs=74
I recommend then reviewing the e2e test so that you can see what the new logic does in practice.
Other notable files:
Some changed I will make later but which should not block reviewing:
Author Checklist
All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.
I have...
!
to the type prefix if the change is state-machine breakingThe LastConsensusValidatorSet needs to be initialized via migration to get a correct diff, this is still TODO
Maybe this should target a feature branch?
CHANGELOG.md
Reviewers Checklist
All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.
I have...
!
the type prefix if the change is state-machine breakingSummary by CodeRabbit
New Features
Enhancements
Bug Fixes
Tests
Documentation